package cn.cdqf.coupon.rabbit;

import cn.cdqf.coupon.service.CouponTemplateService;
import cn.cdqf.globle.util.RedisUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.HashMap;

@Component
@Slf4j
public class RabbitReceive {
    //监听的队列配置。监听到消息后，交给 @RabbitHandler修饰的方法处理
    @RabbitListener(
            bindings = @QueueBinding(
                    //申明队列
                    value=@Queue(value = "dispatch_for_template",durable = "true"),
                    //申明交换机,指定主题模式
                    exchange=@Exchange(value="dispatch_for_template",durable = "true",type = "direct"
                            //忽略一些基本的申明时会出现的异常
                            ,ignoreDeclarationExceptions = "true"),
                    //路由键申明 topic模式 使用#表示任意多个
                    key="decrTemplate"
            )

    )
    @RabbitHandler
    public void receiverMessage(Message message , Channel channel) throws IOException {

        Object messageId = message.getHeaders().get("messageId");

        HashMap hashMap = objectMapper.readValue(String.valueOf(message.getPayload()), HashMap.class);
        Object couponId = hashMap.get("couponId");
        Object count = hashMap.get("count");
        //判断key是否存在  因为有可能重发了一次了
        boolean consumerLock = redisUtils.hsetnx("rabbit_" + messageId, "consumerLock", System.currentTimeMillis() + 3000 * 5);
        if(consumerLock) {
            //修改状态  为了避免在消费时候又被别人消息重发
            redisUtils.hset("rabbit_"+messageId,"consumerStatus",1);
            long deliveryTag = (long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
            try {
                //加库存
                couponTemplateService.decrByCouponId(String.valueOf(couponId), -Integer.valueOf(String.valueOf(count)));

                //首先取得消息的编号DELIVERY_TAG

                //手动确认
                channel.basicAck(deliveryTag,false);
                //消费成功
                redisUtils.del("rabbit_"+messageId);
                //channel.basicNack(第三个参数就是重回队列);
            }catch (Exception e){
                log.error("在库存回滚业务中出现异常：{}",e);
                //如果没有重回队列就死信队列
                channel.basicNack(deliveryTag,false,false);
                //改成未消费 ，生产端才会重发
                redisUtils.hset("rabbit_"+messageId,"consumerStatus",0);
            } finally {
                //释放锁
                redisUtils.hdel("rabbit_" + messageId, "consumerLock");
            }
        }else{
            //极端情况的补偿
            //死锁解决参考前面的代码  如果担心业务出问题 可以把死锁的时间弄长一点

            //判断key是否存在   因为有可能重发了一次了
        }

        log.info("获得的消息内容为：{}",message.getPayload());



    }
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private CouponTemplateService couponTemplateService;
    @Autowired
    private RedisUtils redisUtils;
}
